Что такое const assertions в generic-функциях TypeScript?

JuniorTypeScript · Frontend·Обновлено 14 сентября 2026
Коротко
const assertion (as const) заставляет TypeScript выводить самый узкий литеральный и readonly-тип значения вместо расширенного базового типа. В generic-функциях это используют, чтобы дженерик выводил литеральные типы аргументов (например, точные строки или readonly-кортежи), а не обобщённые string/number[].

Что такое const assertion

as const — это специальная форма type assertion, которая говорит компилятору: «выведи для этого значения самый узкий из возможных типов и сделай его неизменяемым». Без неё TypeScript расширяет (widening) типы литералов до базовых, потому что по умолчанию предполагает, что значение переменной может измениться.

let a = "admin";        // тип: string
let b = "admin" as const; // тип: "admin"

const obj = { role: "admin" };        // { role: string }
const obj2 = { role: "admin" } as const; // { readonly role: "admin" }

const arr = [1, 2, 3];        // number[]
const arr2 = [1, 2, 3] as const; // readonly [1, 2, 3]

Почему widening мешает в generic функциях

Generic-функция выводит тип параметра T на основе переданного аргумента. Если аргумент — литерал без as const, TypeScript расширит его тип ещё до вывода T, и вся точность потеряется.

function identity<T>(value: T): T {
  return value;
}

const r1 = identity("admin");      // T = string
const r2 = identity("admin" as const); // T = "admin"

Это особенно важно для функций, которые строят кортежи, объекты-конфиги или API с точными строковыми литералами (например, роутинг, экшены редьюсера, enum-подобные строки).

Практический пример: сохранение кортежа

function tuple<T extends readonly unknown[]>(...items: T): T {
  return items;
}

const withoutConst = tuple(1, "a", true);
// T = (string | number | boolean)[] без as const

function tupleConst<const T extends readonly unknown[]>(...items: T): T {
  return items;
}

const withConst = tupleConst(1, "a", true);
// T = readonly [1, "a", true] — точные литералы и readonly

В TypeScript 5.0 появилась модификатор const прямо у type parameter (<const T>), который автоматически применяет поведение as const ко всем аргументам, выведенным в этот параметр, — не нужно расставлять as const на каждом вызове вручную.

Зачем это нужно на практике

  • Литеральные union-типы вместо string — точные автокомплиты и защита от опечаток.
  • readonly массивы и объекты — компилятор не даст случайно мутировать конфиг.
  • Точный вывод кортежей вместо массивов произвольной длины — полезно для builder-функций, роутеров, схем валидации.
  • Совместим с satisfies, который часто используют вместе с as const, чтобы проверить форму объекта, не теряя литеральные типы.

Ограничения

  • as const — это только компиляторная подсказка, на рантайм она не влияет.
  • Делает объект/массив readonly на уровне типов — попытка мутации будет ошибкой компиляции, но не рантайма.
  • <const T> у generic-параметра работает начиная с TypeScript 5.0.
  • Не решает проблему widening для let-переменных внутри функции — эффект действует только в точке, где применена ассерция.

Что хочет услышать интервьюер

Объяснение widening (расширения) литеральных типов и того, как const assertion его отключает

Понимание разницы между `as const` на значении и модификатором `const` у type parameter (`<const T>`, TS 5.0+)

Пример, где generic-функция без const assertion теряет точность типа (string вместо литерала, number[] вместо кортежа)

Знание, что const assertion делает объекты/массивы readonly только на уровне типов, без влияния на рантайм

Связь с практическими кейсами: builder-функции, конфиги, роутинг, action-типы редьюсера

Пример: Widening без и с as const

function identity<T>(value: T): T {
  return value;
}

const withoutConst = identity("admin");
// тип: string

const withConst = identity("admin" as const);
// тип: "admin"

Пример: const type parameter (TypeScript 5.0+)

function tupleConst<const T extends readonly unknown[]>(...items: T): T {
  return items;
}

const result = tupleConst(1, "a", true);
// T = readonly [1, "a", true]

Пример: as const + satisfies для конфигов

const routes = {
  home: "/",
  profile: "/profile",
} as const satisfies Record<string, string>;

type RouteKey = keyof typeof routes;
// "home" | "profile"

Типичные ошибки

Путают `as const` с обычным type assertion `as SomeType` — считают, что это принудительное приведение типа, а не сужение вывода

Не понимают, зачем нужен readonly, и пытаются мутировать массив/объект после `as const`, получая ошибку компиляции

Забывают, что без `as const` generic-функция выведет расширенный тип (`string`, `number[]`) вместо литерала или кортежа

Считают, что const assertion как-то влияет на рантайм-поведение или производительность

Не знают о синтаксисе `<const T>` у generic-параметров (TS 5.0+) и вручную расставляют `as const` на каждый вызов

Лучшие курсы по теме

изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Feature-Sliced Design

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее